package services;

import helpers.ConsoleConstants;
import helpers.ConsoleUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import models.Company;
import models.Project;
import models.Resource;
import models.ResourceType;
import models.Setting;

public class TestResourceService extends ResourceService {

	public static final String RESOURCE_FILE_EXTENSION = ".txt";

	private final ProjectService projectService = ProjectService.getService();
	private final SettingService settingService = SettingService.getService();

	private static TestResourceService service;

	// Singleton pattern
	private TestResourceService() {

	}

	public static TestResourceService getService() {
		if (service == null)
			service = new TestResourceService();
		return service;
	}

	// FIXME it need to have company criteria
	public List<Resource> getAllResources() {
		return Resource.find.where().eq("type", ResourceType.TestResource).findList();
	}

	public Resource getResource(long resourceId) {
		return Resource.find.byId(resourceId);
	}

	public boolean resourceExists(long resourceId) {
		return getResource(resourceId) != null;
	}

	/**
	 * Add Resource entity of type TestResource in DB and persist configuration to a file system using resource path and id as a file name
	 * 
	 * @param testResource
	 * @param content
	 * @return
	 */
	public Resource saveResource(Company company, Resource testResource, String fileName, String content) {
		Resource result = testResource;
		if (testResource.getId() <= 0) {
			result = createResource(company, testResource);
		} else {
			result = updateResource(testResource);
		}
		storeResourceFile(company, result, fileName, content);
		return result;
	}

	/**
	 * Add Resource entity of type config in DB and persist configuration to a file system using resource path and id as a file name
	 * 
	 * @param testResource
	 * @param content
	 * @return
	 */
	public Resource saveResource(Project project, Resource testResource, String fileName, String content) {
		Resource result = testResource;
		if (testResource.getId() <= 0) {
			result = createResource(project, testResource);
		} else {
			result = updateResource(testResource);
		}
		storeResourceFile(project, result, fileName, content);
		return result;
	}

	private Resource createResource(Company company, Resource testResource) {
		testResource.setCompany(company);
		return createResource(testResource);
	}

	private Resource createResource(Project project, Resource testResource) {
		testResource.setProject(project);
		return createResource(testResource);
	}

	private Resource createResource(Resource testResource) {
		Date currentDate = new Date();
		testResource.setType(ResourceType.TestResource);
		testResource.setCreatedDate(currentDate);
		testResource.setLastModifiedDate(currentDate);
		testResource.save();
		return testResource;
	}

	private Resource updateResource(Resource testResource) {
		testResource.setLastModifiedDate(new Date());
		testResource.save();
		return testResource;
	}

	/**
	 * Fetch test resource content from a resource file
	 * 
	 * @param testResource
	 * @return
	 */
	public String getResourceFileContent(Resource testResource) {
		String content = "";
		if (testResource != null && testResource.getResourcePath() != null) {
			File configFile = new File(testResource.getResourcePath());
			if (configFile.exists()) {
				BufferedReader reader = null;
				try {
					reader = new BufferedReader(new FileReader(configFile));
					StringBuilder builder = new StringBuilder();
					String line = reader.readLine();

					while (line != null) {
						builder.append(line).append("\n");
						line = reader.readLine();
					}
					content = builder.toString();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					if (reader != null) {
						try {
							reader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}
		return content;
	}

	/**
	 * Persist resource to resource file
	 * 
	 * @param content
	 * @param testResource
	 */
	public void storeResourceFile(final Company company, Resource testResource, String fileName, String content) {
		if (testResource == null)
			return;
		if (company == null)
		{
			storeResourceFile(testResource, fileName, content);
			return;
		}
		
		BufferedWriter writer = null;
		try {
			File resourceFile = null;
			if (testResource.getResourcePath() == null) {
				File directory = new File(getResourceDirectory(company));
				if (!directory.exists()) {
					directory.mkdirs();
				}

				if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
					fileName = getResourceFileName(testResource);
				}
				if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
					fileName += RESOURCE_FILE_EXTENSION;
				}
				resourceFile = new File(directory, fileName);

				testResource.setResourcePath(resourceFile.getAbsolutePath());
				testResource.save();
			} else {
				resourceFile = new File(testResource.getResourcePath());
			}
			if (resourceFile.exists()) {
				// Keep backup one last revision only
				File backupFile = new File(resourceFile.getAbsolutePath() + ".bak");
				if (backupFile.exists()) {
					backupFile.delete();
				}

				if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
					fileName = resourceFile.getName();
				}
				if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
					fileName += RESOURCE_FILE_EXTENSION;
				}

				// String resourceFileName = getResourceFileName(testResource);
				if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
					backupFile = new File(resourceFile.getParent(), fileName + ".bak");
				}
				resourceFile.renameTo(backupFile);

				if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
					resourceFile = new File(resourceFile.getParent(), fileName);
					testResource.setResourcePath(resourceFile.getAbsolutePath());
					testResource.save();
				}
			}
			writer = new BufferedWriter(new FileWriter(resourceFile));
			if (content != null) {
				content = content.replaceAll("\r\n|\r", "\n");
			}
			writer.write(content);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}

	/**
	 * Persist resource to config file
	 * 
	 * @param content
	 * @param testResource
	 */
	public void storeResourceFile(Project project, Resource testResource, String fileName, String content) {
		if (project != null && testResource != null) {
			BufferedWriter writer = null;
			try {
				File resourceFile = null;
				if (testResource.getResourcePath() == null) {
					File directory = new File(getResourceDirectory(project));
					if (!directory.exists()) {
						directory.mkdirs();
					}

					if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
						fileName = getResourceFileName(testResource);
					}
					if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
						fileName += RESOURCE_FILE_EXTENSION;
					}
					resourceFile = new File(directory, fileName);

					testResource.setResourcePath(resourceFile.getAbsolutePath());
					testResource.save();
				} else {
					resourceFile = new File(testResource.getResourcePath());
				}
				if (resourceFile.exists()) {
					// Keep backup one last revision only
					File backupFile = new File(resourceFile.getAbsolutePath() + ".bak");
					if (backupFile.exists()) {
						backupFile.delete();
					}

					if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
						fileName = resourceFile.getName();
					}
					if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
						fileName += RESOURCE_FILE_EXTENSION;
					}

					// String resourceFileName = getResourceFileName(testResource);
					if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
						backupFile = new File(resourceFile.getParent(), fileName + ".bak");
					}
					resourceFile.renameTo(backupFile);

					if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
						resourceFile = new File(resourceFile.getParent(), fileName);
						testResource.setResourcePath(resourceFile.getAbsolutePath());
						testResource.save();
					}
				}
				writer = new BufferedWriter(new FileWriter(resourceFile));
				if (content != null) {
					content = content.replaceAll("\r\n|\r", "\n");
				}
				writer.write(content);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * Replace non numeric and characters with underscore
	 * 
	 * @param testResource
	 * @return
	 */
	private String getResourceFileName(Resource testResource) {
		return testResource.getName().replaceAll("[^A-Za-z0-9\\-_ ]+", "_") + RESOURCE_FILE_EXTENSION;
	}

	public Resource getResource(Company company, String resourceName) {
		
		List<Resource> testResources = Resource.find.where().eq("company.id", company.getId()).eq("name", resourceName).eq("type", ResourceType.TestResource).findList();

		if (testResources == null || testResources.isEmpty())
			return null;
		else {
			// FIXME choose the one without having no relation with other projects
			return testResources.get(0);
		}

	}

	public Resource getResource(Project project, String resourceName) {
		return Resource.findResourceByProject(project, ResourceType.TestResource, resourceName);
	}

	public String getResourceDirectory(Company company) {
		String directory = null;
		if (company != null) {
			Setting setting = settingService.getSetting(Setting.CUSTOMERS_ROOT_DIRECTORY);
			if (setting != null) {
				directory = ConsoleUtils.getPath(new String[] { setting.getStringValue(), company.getCompanyId(), ConsoleConstants.RESOURCES_DIRECTORY });
			}
		}
		return directory;
	}

	public String getResourceDirectory(Project project) {
		String directory = null;
		if (project != null) {
			Setting setting = settingService.getSetting(Setting.CUSTOMERS_ROOT_DIRECTORY);
			if (setting != null) {
				directory = ConsoleUtils.getPath(new String[] { projectService.getProjectDirectory(project), ConsoleConstants.RESOURCES_DIRECTORY });
			}
		}
		return directory;
	}
	/**
	 * Get global resource with specific name
	 * @param resourceName
	 * @return
	 */
	public Resource getResource(String resourceName)
	{
		List<Resource> testResources = Resource.find.where().isNull("company.id").eq("name", resourceName).eq("type", ResourceType.TestResource).findList();

		if (testResources == null || testResources.isEmpty())
			return null;
		else {
			return testResources.get(0);
		}
		
	}
	/**
	 * Save global resource which can be used for every company
	 * @param testResource
	 * @param fileName
	 * @param content
	 * @return
	 */
	public Resource saveResource(Resource testResource, String fileName,
			String content)
	{
		Resource result = testResource;
		if (testResource.getId() <= 0) {
			result = createResource(testResource);
		} else {
			result = updateResource(testResource);
		}
		storeResourceFile(result, fileName, content);
		return result;
	}
	/**
	 * Store global resource file
	 * @param testResource
	 * @param content
	 */
	private void storeResourceFile(Resource testResource, String fileName, String content)
	{
		if ( testResource != null) {
			BufferedWriter writer = null;
			try {
				File resourceFile = null;
				if (testResource.getResourcePath() == null) {
					File directory = new File(getGlobalResourceDirectory());
					if (!directory.exists()) {
						directory.mkdirs();
					}

					if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
						fileName = getResourceFileName(testResource);
					}
					if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
						fileName += RESOURCE_FILE_EXTENSION;
					}
					resourceFile = new File(directory, fileName);

					testResource.setResourcePath(resourceFile.getAbsolutePath());
					testResource.save();
				} else {
					resourceFile = new File(testResource.getResourcePath());
				}
				if (resourceFile.exists()) {
					// Keep backup one last revision only
					File backupFile = new File(resourceFile.getAbsolutePath() + ".bak");
					if (backupFile.exists()) {
						backupFile.delete();
					}

					if (StringUtils.isBlank(fileName) || fileName.trim().equalsIgnoreCase(RESOURCE_FILE_EXTENSION)) {
						fileName = resourceFile.getName();
					}
					if (!fileName.endsWith(RESOURCE_FILE_EXTENSION)) {
						fileName += RESOURCE_FILE_EXTENSION;
					}

					// String resourceFileName = getResourceFileName(testResource);
					if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
						backupFile = new File(resourceFile.getParent(), fileName + ".bak");
					}
					resourceFile.renameTo(backupFile);

					if (!fileName.equalsIgnoreCase(resourceFile.getName())) {
						resourceFile = new File(resourceFile.getParent(), fileName);
						testResource.setResourcePath(resourceFile.getAbsolutePath());
						testResource.save();
					}
				}
				writer = new BufferedWriter(new FileWriter(resourceFile));
				if (content != null) {
					content = content.replaceAll("\r\n|\r", "\n");
				}
				writer.write(content);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		
	}
	/**
	 * Return global resource directory
	 * @return
	 */
	private String getGlobalResourceDirectory()
	{
		String directory = null;
		Setting setting = settingService.getSetting(Setting.CUSTOMERS_ROOT_DIRECTORY);
		if (setting != null) {
			directory = ConsoleUtils.getPath(new String[] { setting.getStringValue(), ConsoleConstants.COMMON_DIRECTORY, ConsoleConstants.RESOURCES_DIRECTORY });
		}
		return directory;
	}
}
